{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# AppBuilder-SDK 指定工具调用使用实例\n",
    "\n",
    "* 注意：当前功能为试运行阶段，可能存在如下问题，如使用过程遇到其他问题，欢迎提issue或微信群讨论。\n",
    "\n",
    "  * 需开启\"组件/知识库结论可直接作为回复\"\n",
    "\n",
    "  * 组件名称不是界面上的原始名字，而是个人空间组件列表中的英文名\n",
    "\n",
    "  * 自定义组件的参数不能使用系统参数，可以使用用户添加的参数\n",
    "\n",
    "  * 部分官方组件使用的参数与界面上的参数不一致\n",
    "\n",
    "## 一、背景介绍\n",
    "\n",
    "指定工具调用是为了方便更多用户在AppBuilder官网创建应用之后，希望能直接调用自己定义的工作流或者官方的组件并应用于自己的业务中。具体的调用方法如下面的教程所示。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 二、实操流程\n",
    "\n",
    "\n",
    "### 2.1 前置操作简述\n",
    "\n",
    "- 【必须】登录[百度智能云千帆AppBuilder官网](https://cloud.baidu.com/product/AppBuilder)创建账户。\n",
    "- 【必须】在[百度智能云千帆AppBuilder控制台](https://console.bce.baidu.com/ai_apaas/dialogHome)左侧菜单栏『我的密钥』页面获取密钥，并复制。\n",
    "- 【必须】在python3.9及以上的环境中安装`appbuilder-sdk`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2、创建应用\n",
    "\n",
    "\n",
    "##### 2.2.1、点击创建应用界面\n",
    "点击[百度智能云千帆AppBuilder控制台](https://console.bce.baidu.com/ai_apaas/dialogHome)左侧菜单栏『创建应用』，开始我们的系统应用。\n",
    "\n",
    "\n",
    "#### 2.2.2、填写应用信息，选择工具组件\n",
    "\n",
    "我们需要首先配置该应用的基本信息，包括 名称、描述、角色指令、开场白、推荐问、能力组件等。\n",
    "\n",
    "在这里我们首先选择一个组件工具，比如天气查询组件。\n",
    "\n",
    "<img src=\"https://bj.bcebos.com/v1/test-tl/toolchoice_create.jpg?authorization=bce-auth-v1%2F6148abe36db84938886e533121ff9628%2F2024-09-23T02%3A47%3A19Z%2F-1%2Fhost%2F3d4b3940d22aa1090a1ffcd9d47e715b526b372b1e9fd4242f1d77ebdde633b6\" alt=\"drawing\" width=\"1000\"/>\n",
    "\n",
    "点击图里组件上方\"应用回复设置\"，打开\"组件/知识库结论可直接作为回复\"。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.2.3、发布应用\n",
    "\n",
    "我们可以在页面右上角『发布』按钮发布该应用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.2.4 获取已发布应用的ID\n",
    "\n",
    "在 [百度智能云千帆AppBuilder控制台-我的应用](https://console.bce.baidu.com/ai_apaas/app)页面中，可以查看已发布应用的ID，我们复制该ID，开始后续的代码态操作。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 使用Python SDK调用已发布App\n",
    "\n",
    "当应用已经发布后，我们可以通过SDK在代码态调用，方便用户集成到自己的系统中，通过自己的系统对外提供服务。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.1 引入AppBuilder-SDK，设置TOKEN，设置APPID"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AppBuilder 模块导入成功！\n",
      "您的AppBuilder Token为：your_appbuilder_token\n",
      "您的AppBuilder App ID为：your_publish_app_id\n"
     ]
    }
   ],
   "source": [
    "# 引入os模块，引入appbuilder 模块\n",
    "import os\n",
    "import appbuilder\n",
    "\n",
    "# 设置appbuilder的token密钥，从页面上复制粘贴我的密钥，覆盖此处的 \"your_appbuilder_token\"\n",
    "os.environ['APPBUILDER_TOKEN'] = \"your_appbuilder_token\"\n",
    "# 设置需要调用的app，从页面上复制粘贴应用ID，覆盖此处的 \"your_publish_app_id\"\n",
    "app_id = \"your_publish_app_id\"\n",
    "\n",
    "print(\"AppBuilder 模块导入成功！\")\n",
    "print(\"您的AppBuilder Token为：{}\".format(os.environ['APPBUILDER_TOKEN']))\n",
    "print(\"您的AppBuilder App ID为：{}\".format(app_id))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.2 初始化Agent实例，创建会话并对话\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 基于app_id初始化Agent\n",
    "builder = appbuilder.AppBuilderClient(app_id)\n",
    "# 创建会话ID\n",
    "conversation_id = builder.create_conversation()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.3 正常创建会话并对话\n",
    "\n",
    "- 正常对话下(不采用tool_choice模式)组件的调用受到大模型的控制，参数由大模型生成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#在不采用tool choice的情况下，正常对话APP应用依赖于应用的大模型选择调用组件，而不是直接与组件交互\n",
    "st = builder.run(conversation_id=conversation_id, query=\"北京今天的天气\", stream=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "answer='' events=[Event(code=0, message='', status='done', event_type='function_call', content_type='function_call', detail={'text': {'arguments': {'city': '北京'}, 'component_code': 'WeatherQuery', 'component_name': '天气查询'}}, usage=Usage(prompt_tokens=513, completion_tokens=37, total_tokens=550, name='Qianfan-Appbuilder-Speed-8k'), tool_calls=None)]\n"
     ]
    }
   ],
   "source": [
    "# function_call路由到天气查询组件执行得到组件结果，最终经过总结得到最终结果\n",
    "#下面结果中天气组件的参数({'city': '北京'})由大模型生成\n",
    "for k in st.content:\n",
    "    print(k)\n",
    "    break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.4 tool_choice方式指定工具参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "#导入指定工具参数类\n",
    "from appbuilder.core.console.appbuilder_client.data_class import ToolChoiceFunction, ToolChoice\n",
    "#建立基本的工具调用对象，包括工具名字与参数传递\n",
    "tool_choice_function = ToolChoiceFunction(name=\"WeatherQuery\", input={\"city\": \"北京天气\"})\n",
    "tool_choice = ToolChoice(type=\"function\", function=tool_choice_function)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.5 tool_choice方式实现对话应用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "#请求指定工具tool_choice\n",
    "st = builder.run(conversation_id=conversation_id, query=\"北京今天的天气\", tool_choice=tool_choice, stream=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.6 tool_choice工具调用展示\n",
    "\n",
    "- 当前模式下由于是直接调用组件，组件的输入参数由输入参数控制"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "answer='' events=[Event(code=0, message='', status='done', event_type='function_call', content_type='function_call', detail={'text': {'arguments': {'city': '北京天气'}, 'component_code': 'WeatherQuery', 'component_name': '天气查询'}}, usage=None, tool_calls=None)]\n"
     ]
    }
   ],
   "source": [
    "#可以看出，当前天气查询组件的输入参数query({'city': '北京天气'})由tool_choice控制\n",
    "#此时直接调用天气调用组件而不是大模型function call选择天气组件提供参数\n",
    "for k in st.content:\n",
    "    print(k)\n",
    "    break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.7 tool_choice完整结果展示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "answer='' events=[Event(code=0, message='', status='done', event_type='function_call', content_type='function_call', detail={'text': {'arguments': {'city': '北京天气'}, 'component_code': 'WeatherQuery', 'component_name': '天气查询'}}, usage=None, tool_calls=None)]\n",
      "answer='' events=[Event(code=0, message='', status='preparing', event_type='WeatherQuery', content_type='status', detail={}, usage=None, tool_calls=None)]\n",
      "answer='' events=[Event(code=0, message='', status='done', event_type='WeatherQuery', content_type='text', detail={'text': '北京  09月23日(星期一) 08:05更新  15.6 ℃  晴 24℃/12℃  东北风1级 | 湿度74%  未来2小时不会下雨 > 北京今明天云量逐渐增多 明夜有小雨 > 堪比台风降雨!冷空气“撞”上季风 华南暴雨大暴雨持续 局地特大暴雨 推荐 图集 昨天 9/22 23/11℃  今天 9/23 24/12℃  星期二 9/24 24/15℃  48小时预报 09:00 11:00 13:00 15:00 17:00 19:00 21:00 23:00 01:00 03:00 05:00 07:00 09:00 11:00 13:00 15:00 17:00 19:00 21:00 23:00 01:00 03:00 05:00 07:00 09:00 17℃20℃22℃23℃21℃20℃17℃16℃14℃13℃12℃13℃16℃21℃23℃23℃23℃21℃19℃18℃16℃15℃15℃16℃19℃ <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级 15天预报 长期预报 历史天气  今天 9/23 周二 9/24 周三 9/25 周四 9/26 周五 9/27 周六 9/28 周日 9/29 周一 9/30 周二 10/1 周三 10/2 周四 10/3 周五 10/4 周六 10/5 周日 10/6 周一 10/7 晴 多云 多云 多云 多云 多云 小雨 雨 阴 晴 阴 晴 阴 阴 雨 24°24°25°24°25°24°23°16°21°24°23°24°24°25°24°  12°15°14°15°16°16°14°10°13°14°15°16°17°18°16°  多云 小雨 多云 多云 多云 小雨 小雨 晴 多云 阴 多云 阴 阴 阴 雨 查看详情 40天预报  温度趋势 温暖  降水趋势 8次降水  生活指数  穿衣 衬衫  跑步 适宜  钓鱼 适宜  洗车 较适宜  晾晒 适宜  污染扩散指数 中  感冒指数 少发  出行晴雨 沿途天气  我的天空  精彩推荐  堪比台风降雨!华南暴雨大暴雨持续  新闻联播天气预报 全国中秋假期出游天气地图出炉  北京蓝天白云“颜值”在线  秋分:暑退秋澄气转凉 日光夜色两均长  晨味时节之白露  风雨潮“三碰头”  堪比台风降雨!华南暴雨大暴雨持续  新闻联播天气预报 全国中秋假期出游天气地图出炉  北京蓝天白云“颜值”在线  秋分:暑退秋澄气转凉 日光夜色两均长  晨味时节之白露  风雨潮“三碰头”  堪比台风降雨!华南暴雨大暴雨持续  新闻联播天气预报 全国中秋假期出游天气地图出炉  北京蓝天白云“颜值”在线  秋分:暑退秋澄气转凉 日光夜色两均长  晨味时节之白露  风雨潮“三碰头”  未来3天公报 未来10天公报  推荐 直播 图集 短视频 生活  北京今明天云量逐渐增多 明夜有小雨2024-09-23 07:24'}, usage=None, tool_calls=None)]\n",
      "answer='' events=[Event(code=0, message='', status='success', event_type='WeatherQuery', content_type='status', detail={}, usage=None, tool_calls=None)]\n",
      "answer='' events=[Event(code=0, message='', status='done', event_type='function_call', content_type='function_call', detail={'text': {'arguments': {'origin_query': '北京今天的天气'}, 'component_code': 'ChatAgent', 'component_name': '聊天助手'}}, usage=None, tool_calls=None)]\n",
      "answer='' events=[Event(code=0, message='', status='preparing', event_type='ChatAgent', content_type='status', detail={}, usage=None, tool_calls=None)]\n",
      "answer='北京今天的天气情况如下' events=[Event(code=0, message='', status='running', event_type='ChatAgent', content_type='text', detail={'text': '北京今天的天气情况如下'}, usage=Usage(prompt_tokens=1105, completion_tokens=0, total_tokens=1105, name='Qianfan-Appbuilder-Speed-8k'), tool_calls=None)]\n",
      "answer='：\\n\\n- **日期**：2024年9月23日(星期一)\\n- **天气**：晴\\n- **温度**：最高温度24℃，' events=[Event(code=0, message='', status='running', event_type='ChatAgent', content_type='text', detail={'text': '：\\n\\n- **日期**：2024年9月23日(星期一)\\n- **天气**：晴\\n- **温度**：最高温度24℃，'}, usage=Usage(prompt_tokens=1105, completion_tokens=0, total_tokens=1105, name='Qianfan-Appbuilder-Speed-8k'), tool_calls=None)]\n",
      "answer='最低温度12℃\\n- **风向**：东北风1级\\n- **湿度**：74%\\n- **未来2小时**：不会下雨\\n- **今天云量**：逐渐增多\\n- **明天天气**：明夜有小雨\\n\\n如果您需要更多详细信息或未来几天的天气预报，请告诉我！' events=[Event(code=0, message='', status='running', event_type='ChatAgent', content_type='text', detail={'text': '最低温度12℃\\n- **风向**：东北风1级\\n- **湿度**：74%\\n- **未来2小时**：不会下雨\\n- **今天云量**：逐渐增多\\n- **明天天气**：明夜有小雨\\n\\n如果您需要更多详细信息或未来几天的天气预报，请告诉我！'}, usage=Usage(prompt_tokens=1105, completion_tokens=0, total_tokens=1105, name='Qianfan-Appbuilder-Speed-8k'), tool_calls=None)]\n",
      "answer='' events=[Event(code=0, message='', status='done', event_type='ChatAgent', content_type='text', detail={'text': ''}, usage=Usage(prompt_tokens=1105, completion_tokens=114, total_tokens=1219, name='Qianfan-Appbuilder-Speed-8k'), tool_calls=None)]\n",
      "answer='' events=[Event(code=0, message='', status='success', event_type='ChatAgent', content_type='status', detail={}, usage=None, tool_calls=None)]\n",
      "answer='' events=[]\n"
     ]
    }
   ],
   "source": [
    "st = builder.run(conversation_id=conversation_id, query=\"北京今天的天气\", tool_choice=tool_choice, stream=True)\n",
    "for k in st.content:\n",
    "    print(k)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.4 使用Java SDK调用已发布的App\n",
    "##### 2.4.1 ToolChoice强制命中组件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "package org.example;\n",
    "\n",
    "import java.io.IOException;\n",
    "import java.util.*;\n",
    "\n",
    "import com.google.gson.annotations.SerializedName;\n",
    "\n",
    "import com.baidubce.appbuilder.base.exception.AppBuilderServerException;\n",
    "import com.baidubce.appbuilder.console.appbuilderclient.AppBuilderClient;\n",
    "import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientIterator;\n",
    "import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientResult;\n",
    "import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientRunRequest;\n",
    "import com.baidubce.appbuilder.model.appbuilderclient.Event;\n",
    "import com.baidubce.appbuilder.base.utils.json.JsonUtils;\n",
    "\n",
    "class AppBuilderClientDemo {\n",
    "\n",
    "    public static void main(String[] args) throws IOException, AppBuilderServerException {\n",
    "        System.setProperty(\"APPBUILDER_TOKEN\", \"请设置正确的应用密钥\");\n",
    "        String appId = \"请设置正确的应用ID\";\n",
    "        AppBuilderClient builder = new AppBuilderClient(appId);\n",
    "        String conversationId = builder.createConversation();\n",
    "\n",
    "        AppBuilderClientRunRequest request = new AppBuilderClientRunRequest();\n",
    "        request.setAppId(appId);\n",
    "        request.setConversationID(conversationId);\n",
    "        request.setQuery(\"北京今天的天气\");\n",
    "        request.setStream(false);\n",
    "        request.setEndUserId(\"java_toolchoice_demo\");\n",
    "\n",
    "        // 注意使用创建应用中用到的组件。名称、参数均以实际使用的组件为准。\n",
    "        Map<String, Object> input = new HashMap<>();\n",
    "        input.put(\"city\", \"北京\");\n",
    "        AppBuilderClientRunRequest.ToolChoice.Function func = new AppBuilderClientRunRequest.ToolChoice.Function(\n",
    "                \"WeatherQuery\", input);\n",
    "\n",
    "        AppBuilderClientRunRequest.ToolChoice choice = new AppBuilderClientRunRequest.ToolChoice(\"function\", func);\n",
    "        request.setToolChoice(choice);\n",
    "\n",
    "        AppBuilderClientIterator itor = builder.run(request);\n",
    "        while (itor.hasNext()) {\n",
    "            AppBuilderClientResult result = itor.next();\n",
    "            System.out.println(result);\n",
    "        }\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.5 使用Go SDK调用已发布的应用\n",
    "##### 2.5.1 ToolChoice强制命中组件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "go"
    }
   },
   "outputs": [],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    "\t\"io\"\n",
    "\t\"os\"\n",
    "\n",
    "\t\"github.com/baidubce/app-builder/go/appbuilder\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\t// 设置APPBUILDER_TOKEN、GATEWAY_URL_V2环境变量\n",
    "\tos.Setenv(\"APPBUILDER_TOKEN\", \"请设置正确的应用密钥\")\n",
    "\t// 默认可不填，默认值是 https://qianfan.baidubce.com\n",
    "\tos.Setenv(\"GATEWAY_URL_V2\", \"\")\n",
    "\tconfig, err := appbuilder.NewSDKConfig(\"\", \"\")\n",
    "\tif err != nil {\n",
    "\t\tfmt.Println(\"new config failed: \", err)\n",
    "\t\treturn\n",
    "\t}\n",
    "\t// 初始化实例\n",
    "\tappID := \"请填写正确的应用ID\"\n",
    "\tbuilder, err := appbuilder.NewAppBuilderClient(appID, config)\n",
    "\tif err != nil {\n",
    "\t\tfmt.Println(\"new agent builder failed: \", err)\n",
    "\t\treturn\n",
    "\t}\n",
    "\t// 创建对话ID\n",
    "\tconversationID, err := builder.CreateConversation()\n",
    "\tif err != nil {\n",
    "\t\tfmt.Println(\"create conversation failed: \", err)\n",
    "\t\treturn\n",
    "\t}\n",
    "\n",
    "  // 注意使用创建应用中用到的组件。名称、参数均以实际使用的组件为准。\n",
    "\tinput := make(map[string]any)\n",
    "\tinput[\"city\"] = \"北京\"\n",
    "\tend_user_id := \"go_toolchoice_demo\"\n",
    "\ti, err := client.Run(AppBuilderClientRunRequest{\n",
    "\t\tConversationID: conversationID,\n",
    "\t\tAppID:          appID,\n",
    "\t\tQuery:          \"\",\n",
    "\t\tEndUserID:      &end_user_id,\n",
    "\t\tStream:         false,\n",
    "\t\tToolChoice: &ToolChoice{\n",
    "\t\t\tType: \"function\",\n",
    "\t\t\tFunction: ToolChoiceFunction{\n",
    "\t\t\t\tName:  \"WeatherQuery\",\n",
    "\t\t\t\tInput: input,\n",
    "\t\t\t},\n",
    "\t\t},\n",
    "\t})\n",
    "\n",
    "\tif err != nil {\n",
    "\t\tfmt.Println(\"run failed: \", err)\n",
    "\t\treturn\n",
    "\t}\n",
    "\n",
    "    for answer, err := i.Next(); err == nil; answer, err = i.Next() {\n",
    "\t\tfor _, ev := range answer.Events {\n",
    "\t\t\tevJSON, _ := json.Marshal(ev)\n",
    "\t\t\tfmt.Println(string(evJSON))\n",
    "\t\t}\n",
    "\t}\n",
    "}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  },
  "vscode": {
   "interpreter": {
    "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
